package ip

import (
	"fmt"
	"math"
	"net"
)

type Local struct {
}

type Addr struct {
	Ip4, Ip6 net.IP
}

type Provider interface {
	ObtainIp() *Addr
}

func (i *Addr) String() string {
	return fmt.Sprintf("ipv4=%s ipv6=%s", i.Ip4.String(), i.Ip6.String())
}

func GetIpByType(network, rAddr string) net.IP {
	conn, err := net.Dial(network, rAddr)
	if err != nil {
		return nil
	}
	defer conn.Close()
	addr := conn.LocalAddr().(*net.UDPAddr)
	return addr.IP
}
func (l *Local) ObtainIp() (r *Addr) {
	r = &Addr{}
	r.Ip6 = GetIpByType("udp6", "[2400:3200::1]:53")
	r.Ip4 = GetIpByType("udp4", "223.5.5.5:53")

	if r.Ip6 == nil {
		return
	}
	ifa, err := net.Interfaces()
	if err != nil {
		return
	}
	var vv *net.Interface
	ip6Addrs := make([]net.Addr, 0)
	for i := range ifa {
		v := &ifa[i]
		addrs, _ := v.Addrs()
		if addrs == nil {
			v = nil
			continue
		}
		for _, addr := range addrs {
			if ipnet, ok := addr.(*net.IPNet); ok {
				if r.Ip6.Equal(ipnet.IP) {
					vv = v
					break
				}
			}
		}
		if vv != nil {
			for _, addr := range addrs {
				if ipnet, ok := addr.(*net.IPNet); ok {
					if len(ipnet.Mask) == net.IPv6len && ipnet.IP[0] != 0xfe {
						ip6Addrs = append(ip6Addrs, ipnet)
					}
				}
			}
			break
		}
	}

	if len(ip6Addrs) > 1 {
		var m = math.MaxInt
		for _, v := range ip6Addrs {
			ones, _ := v.(*net.IPNet).Mask.Size()
			if ones < m {
				r.Ip6 = v.(*net.IPNet).IP
				m = ones
			}
		}
	}
	return
}
